package dailynewsreader.controller;

import dailynewsreader.model.EntryComparatorByPubDate;
import dailynewsreader.model.LabelStorage;
import dailynewsreader.model.RSSEntry;
import dailynewsreader.model.RSSFeed;
import dailynewsreader.model.RSSLogo;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * RSS feed feldolgozó osztály
 *
 * @author Zen
 */
public class RSSParser {

    /**
     * Fájl alapján parse-olja a feed-et.
     * 
     * @param file A cache fájl
     * @return A feltöltött és rendezett feed
     * @throws Ha hiba történt a feldolgozás során, akkor eldobjuk, és a
     * hívásnál kezeljük le
     */
    public static ArrayList<RSSFeed> parseRSSFeedsFromFile(File file) throws FileNotFoundException {
        Document doc;
        
        try{
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            doc = builder.parse(file);
        } catch (Exception ex) {
            throw new FileNotFoundException("File not found!");
        }
                
        ArrayList<RSSFeed> feeds = new ArrayList<RSSFeed>();
        NodeList channel = doc.getElementsByTagName("channel");
        for (int i = 0; i < channel.getLength(); i++) {
            Element channelElement = (Element) channel.item(i);
            
            RSSFeed feed = new RSSFeed();
            feed = doParseFeed(feed, channelElement);
            try {
                feed.setFeedURL(new URL(getElementValue(channelElement, "feedurl")));
            } catch (MalformedURLException ex) {}
            
            feeds.add(feed);
        }
        
        return feeds;
    }

    /**
     * URL alapján parse-olja a feed-et.
     *
     * @param feed Az RSS feed objektum
     * @return A feltöltött és rendezett feed
     * @throws Ha hiba történt a feldolgozás során, akkor eldobjuk, és a
     * hívásnál kezeljük le
     */
    public static RSSFeed parseRSSFeedFromURL(RSSFeed feed) throws IOException {
        Document doc;
        
        try{
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            URL u = feed.getFeedURL();
            doc = builder.parse(u.openStream());
        } catch (Exception ex){
            throw new IOException("Nincs Internet kapcsolat!");
        }
        
        NodeList channel = doc.getElementsByTagName("channel");
        if (channel.getLength() == 1) {
            Element channelElement = (Element) channel.item(0);
            feed = doParseFeed(feed, channelElement);
        }
        
        return feed;
    }
    
    /**
     * Parse-olja az RSS-t és beállítja az objektum-reprezentációját. Az
     * feltöltött feed-et csökkenő sorrendbe rendezett bejegyzésekkel adja
     * vissza
     * 
     * @param feed Az RSS feed objektum
     * @param channelElement A csatorna
     * @return A feltöltött és rendezett feed
     */
    private static RSSFeed doParseFeed(RSSFeed feed, Element channelElement) {
        if (LabelStorage.FEED_DEFAULT_TITLE.equals(feed.getTitle()))
            feed.setTitle(getElementValue(channelElement, "title"));
        feed.setLink(getElementValue(channelElement, "link"));
        if (feed.isFavourite()==null) {
            feed.setIsFavorite((getElementValue(channelElement, "isfavourite").equals("true"))?true:false);
        }

        NodeList image = channelElement.getElementsByTagName("image");
        for (int j = 0; j < image.getLength(); j++) {
            Element element = (Element) image.item(j);

            RSSLogo logo = new RSSLogo();
            logo.setTitle(getElementValue(element, "title"));
            logo.setLink(getElementValue(element, "link"));
            logo.setUrl(getElementValue(element, "url"));
            feed.setLogo(logo);
        }

        NodeList nodes = channelElement.getElementsByTagName("item");
        for (int j = 0; j < nodes.getLength(); j++) {
            Element element = (Element) nodes.item(j);

            RSSEntry entry = new RSSEntry();
            entry.setTitle(getElementValue(element, "title"));
            entry.setLink(getElementValue(element, "link"));
            SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
            try {
                entry.setPubDate(sdf.parse(getElementValue(element, "pubDate")));
            } catch (ParseException ex) {
                entry.setPubDate(new Date());
            }
            entry.setAuthor(getElementValue(element, "dc:creator"));
            entry.setDescription(getElementValue(element, "description"));
            entry.setIsRead((getElementValue(element, "isread").equals("true"))?true:false);
            entry.setFeedTitle(feed.getTitle());

            if (!feed.hasThisEntry(entry)) {
                feed.addEntry(entry);
            }
        }

        Collections.sort(feed.getEntries(), Collections.reverseOrder(new EntryComparatorByPubDate()));

        return feed;
    }

    /**
     * Egy DOM elemből visszaadja a szöveges tartalmat
     *
     * @param e A DOM elem
     * @return Az elem String tartalma
     */
    private static String getCharacterDataFromElement(Element e) {
        try {
            Node child = e.getFirstChild();
            if (child instanceof CharacterData) {
                CharacterData cd = (CharacterData) child;
                return cd.getData();
            }
        } catch (Exception ex) {
        }
        return "";
    }

    /**
     * Flloat-tá alakít egy strnig-et
     *
     * @param value Szöveges érték
     * @return A szám érték
     */
    protected static float getFloat(String value) {
        if (value != null && !value.equals("")) {
            return Float.parseFloat(value);
        } else {
            return 0;
        }
    }

    /**
     * Egy elem és egy azonosító alapján visszaadja annak értékét a DOM-ból
     *
     * @param parent A szülő elem a DOM-ban
     * @param label A címke, amit az elemből ki akarunk nyerni
     * @return Az elem tartalma
     */
    protected static String getElementValue(Element parent, String label) {
        return getCharacterDataFromElement((Element) parent.getElementsByTagName(label).item(0));
    }
}
